import { Clock, PerspectiveCamera, Scene, WebGLRenderer } from 'three';

type AnimateTick = {
  tick: (delta: number) => void;
};

const clock = new Clock();
/**
 * 由于我们将使用renderer.render(scene, camera)生成帧，
 * 因此可以肯定的是，我们需要在Loop类中使用camera、scene和renderer，
 * 考虑一个简单的游戏,每次循环运行时，我们都希望通过将它们向前移动一帧来更新所有这些动画
 * 每一个都有一点点, 几乎是肉眼无法看到的微小量，但随着时间的推移会产生流畅的动画效果。
 *
 * Loop.tick方法
 */
class Loop {
  scene: Scene;
  camera: PerspectiveCamera;
  renderer: WebGLRenderer;
  updatables: Array<AnimateTick>; // 需要循环类中的动画对象列表

  constructor(camera: PerspectiveCamera, scene: Scene, renderer: WebGLRenderer) {
    this.camera = camera;
    this.scene = scene;
    this.renderer = renderer;
    this.updatables = [];
  }

  start() {
    this.renderer.setAnimationLoop(() => {
      // tell every animated object to tick forward one frame
      this.tick();

      // render a frame
      this.renderer.render(this.scene, this.camera);
    });
  }

  stop() {
    this.renderer.setAnimationLoop(null);
  }

  // 我们会让每个动画 tick 向前移动一帧。
  // 去中心化 中心化还是去中心化？
  // 在 对象本身上 定义更新每个对象的逻辑。每个对象都将使用自己的通用.tick方法暴露该逻辑。
  // 现在，Loop.tick方法会很简单。每一帧，我们将遍历一个动画对象列表，并告诉它们每个.tick向前一帧

  // 像这样在运行时向现有类添加属性称为 猴子补丁（这里，我们添加.tick到Mesh实例）。
  // 这是常见的做法，在我们简单的应用程序中不会引起任何问题。
  // 但是，我们不应该养成这样粗心大意的习惯，因为在某些情况下它会导致性能问题。我们只允许自己在这里这样做，因为替代方案更复杂。
  tick() {
    // only call the getDelta function once per frame!
    // .getDelta告诉我们自上次调用.getDelta以来已经过去了多少时间
    // 即使有一个强大的GPU和一个像这个单一立方体这样简单的场景，我们也不会达到每秒60帧的精度。有些帧渲染得有点快，有些帧渲染得有点慢。这个是正常的。
    const delta = clock.getDelta();

    // console.log(
    //   `The last frame rendered in ${delta * 1000} milliseconds`,
    // );

    for (const object of this.updatables) {
      object.tick(delta);
    }
  }
}

export { Loop };
